#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：turbo 
@File    ：serializers.py
@Author  ：赵加响
@Date    ：2023/3/31 17:05 
@Desc    ：
"""
import re
from datetime import datetime, timedelta

from django.contrib.auth import login
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from django.utils.translation import gettext_lazy as _

from apps.accounts.handlers import init_menu
from apps.accounts.models import User, SystemSettings
from bases import codes, messages
from bases.resp_format import api_format
from bases.serializers import APIModelSerializer
from constants import LANGUAGE_ZH
from utils.system_settings.constant import PASSWORD_CHANGE_REMINDER_DAYS, INITIAL_PASSWORD, PASSWORD_VALID_DAYS
from utils.system_settings.handler import get_system_setting


class UserSerializer(APIModelSerializer):
    require_fields = ('username', 'name', 'email')
    unique_fields = ('username', 'name', 'email')

    username = serializers.CharField(label='用户名', max_length=150, required=False, allow_blank=True, allow_null=True)
    name = serializers.CharField(label='姓名', max_length=30, required=False, allow_blank=True, allow_null=True)
    email = serializers.CharField(label='邮箱', max_length=100, required=False, allow_blank=True, allow_null=True)
    role_desc = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = User
        fields = ('id', 'username', 'name', 'email', 'avatar', 'valid_date', 'invalid_date', 'init_status',
                  'is_active', 'roles', 'role_desc', 'is_admin')
        read_only_fields = ('id', 'last_login', 'is_superuser', 'first_name', 'last_name', 'is_staff', 'date_joined',
                            'valid_date', 'invalid_date', 'groups', 'user_permissions', 'init_status', 'avatar')

    def get_role_desc(self, obj):
        if self.localization == LANGUAGE_ZH:
            role_list = obj.roles.values_list('name_zh', flat=True).order_by('-is_admin')
        else:
            role_list = obj.roles.values_list('name_en', flat=True).order_by('-is_admin')
        return ', '.join(role_list) if len(role_list) > 0 else ''

    def validate(self, attrs):
        username = attrs.get('username')
        if '@' in username:
            # Translators: 用户名中不可包含@符号
            self.handle_validation('username', _('the username cannot contain the @ symbol'))
        return super().validate(attrs)

    def create(self, validated_data):
        password = get_system_setting(INITIAL_PASSWORD)
        days = get_system_setting(PASSWORD_VALID_DAYS)
        curr_date = datetime.now().date()

        validated_data['is_active'] = True
        validated_data['invalid_date'] = curr_date + timedelta(days=int(days))
        instance = super().create(validated_data)
        instance.set_password(password)
        instance.save()
        return instance


class UserInfoSerializer(APIModelSerializer):
    """
    该Serializer只允许用户获取自己的信息
    """
    role_desc = serializers.SerializerMethodField(read_only=True)
    menu = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = User
        fields = ('id', 'username', 'name', 'email', 'avatar', 'valid_date', 'invalid_date', 'init_status',
                  'is_active', 'roles', 'role_desc', 'is_admin', 'menu')

    def get_role_desc(self, obj):
        if self.localization == LANGUAGE_ZH:
            role_list = obj.roles.values_list('name_zh', flat=True).order_by('-is_admin')
        else:
            role_list = obj.roles.values_list('name_en', flat=True).order_by('-is_admin')
        return ', '.join(role_list) if len(role_list) > 0 else ''

    def get_menu(self, obj):
        return init_menu(self.request)


class UserLoginSerializer(APIModelSerializer):
    role_desc = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = User
        fields = ('id', 'username', 'name', 'email', 'avatar', 'valid_date', 'invalid_date', 'init_status',
                  'is_active', 'roles', 'role_desc', 'is_admin')

    def get_role_desc(self, obj):
        if self.localization == LANGUAGE_ZH:
            role_list = obj.roles.values_list('name_zh', flat=True).order_by('-is_admin')
        else:
            role_list = obj.roles.values_list('name_en', flat=True).order_by('-is_admin')
        return ', '.join(role_list) if len(role_list) > 0 else ''


class SystemSettingsSerializer(APIModelSerializer):
    require_fields = ('code',)
    unique_fields = ('code',)

    value = serializers.CharField(label='系统设定值', max_length=128, allow_null=True, allow_blank=True)
    content = serializers.CharField(label='系统设定说明', max_length=128, allow_null=True, allow_blank=True)

    class Meta:
        model = SystemSettings
        fields = '__all__'
        read_only_fields = ('id', 'code', 'value_type', 'is_active')

    def validate(self, attrs):
        value_type = self.instance.value_type.strip()
        value = attrs.get('value')

        if value_type == 'int':
            if not value.isdigit():
                # Translators: 数据类型不正确
                self.handle_validation('value', _('incorrect data type'))

        return super().validate(attrs)


class APITokenObtainPairSerializer(TokenObtainPairSerializer):
    """
    自定义登录认证，使用自有用户表
    """

    def validate(self, attrs):

        username = attrs['username']
        password = attrs['password']

        code = codes.CODE_20000_OK

        if re.match('^.+@.+$', username):  # 邮箱登录正则
            user = User.objects.filter(email=username).first()
        else:  # 用户名登录
            user = User.objects.filter(username=username).first()

        if user and user.user_type != 'A':
            code = codes.CODE_50012_USER_TYPE_ERROR
            exception = messages.MSG_50012_USER_TYPE_ERROR
            data = api_format(code, False, {}, str(exception))
        elif user and user.is_active and user.check_password(password):
            curr_time = datetime.now()
            reminder = user.invalid_date - curr_time.date()
            reminder_days = get_system_setting(PASSWORD_CHANGE_REMINDER_DAYS)
            if reminder.days < 0:
                code = codes.CODE_50008_PASS_EXPIRED
                exception = messages.MSG_50008_PASS_EXPIRED
                data = api_format(code, False, {}, str(exception))
            elif reminder.days <= int(reminder_days):
                code = codes.CODE_50009_PASS_WILL_EXPIRE
                data = api_format(code, True, {}, '')
            elif user.init_status:
                code = codes.CODE_50010_PASS_INIT_STATUS
                data = api_format(code, True, {}, '')
            else:
                refresh = self.get_token(user)
                serializer = UserLoginSerializer(user, context=self.context)
                request = self.context['request']
                login(request, user)
                data = api_format(code, True, serializer.data, token=str(refresh.access_token))
        elif user and not user.is_active:
            code = codes.CODE_50003_NOT_VALID
            exception = messages.MSG_50003_NOT_VALID
            data = api_format(code, False, {}, str(exception))
        elif user and user.is_active and not user.check_password(password):
            code = codes.CODE_50002_ERROR_PASS
            exception = messages.MSG_50002_ERROR_PASS
            data = api_format(code, False, {}, str(exception))
        else:
            code = codes.CODE_50001_ERROR_USER
            exception = messages.MSG_50001_ERROR_USER
            data = api_format(code, False, {}, str(exception))

        return data


class APIUserLoginTokenObtainPairSerializer(TokenObtainPairSerializer):
    """
    自定义接口用户登录认证，使用自有用户表
    """

    def validate(self, attrs):

        username = attrs['username']
        password = attrs['password']

        code = codes.CODE_20000_OK

        # API用户只允许使用用户名登录获取Token
        user = User.objects.filter(username=username).first()

        if user and user.user_type != 'I':
            code = codes.CODE_50012_USER_TYPE_ERROR
            exception = messages.MSG_50012_USER_TYPE_ERROR
            data = api_format(code, False, {}, str(exception))
        elif user and user.is_active and user.check_password(password):
            refresh = self.get_token(user)
            serializer = UserLoginSerializer(user, context=self.context)
            request = self.context['request']
            login(request, user)
            data = api_format(code, True, serializer.data, token=str(refresh.access_token))
        elif user and not user.is_active:
            code = codes.CODE_50003_NOT_VALID
            exception = messages.MSG_50003_NOT_VALID
            data = api_format(code, False, {}, str(exception))
        elif user and user.is_active and not user.check_password(password):
            code = codes.CODE_50002_ERROR_PASS
            exception = messages.MSG_50002_ERROR_PASS
            data = api_format(code, False, {}, str(exception))
        else:
            code = codes.CODE_50001_ERROR_USER
            exception = messages.MSG_50001_ERROR_USER
            data = api_format(code, False, {}, str(exception))

        return data
